#include <stdio.h>  
#include <stdlib.h> 
#include <string.h> 
 
#include "typedef.h"
#include "ckdes.h"

static const char ip[] = 
{
	58, 50, 42, 34, 26, 18, 10,  2,
	60, 52, 44, 36, 28, 20, 12,  4,
	62, 54, 46, 38, 30, 22, 14,  6,
	64, 56, 48, 40, 32, 24, 16,  8,
	57, 49, 41, 33, 25, 17,  9,  1,
	59, 51, 43, 35, 27, 19, 11,  3,
	61, 53, 45, 37, 29, 21, 13,  5,
	63, 55, 47, 39, 31, 23, 15,  7
};

static const char fp[] =
{
	40,  8, 48, 16, 56, 24, 64, 32,
	39,  7, 47, 15, 55, 23, 63, 31,
	38,  6, 46, 14, 54, 22, 62, 30,
	37,  5, 45, 13, 53, 21, 61, 29,
	36,  4, 44, 12, 52, 20, 60, 28,
	35,  3, 43, 11, 51, 19, 59, 27,
	34,  2, 42, 10, 50, 18, 58, 26,
	33,  1, 41,  9, 49, 17, 57, 25
};

static const char pc1[] = 
{
	57, 49, 41, 33, 25, 17,  9,
	 1, 58, 50, 42, 34, 26, 18,
	10,  2, 59, 51, 43, 35, 27,
	19, 11,  3, 60, 52, 44, 36,

	63, 55, 47, 39, 31, 23, 15,
	 7, 62, 54, 46, 38, 30, 22,
	14,  6, 61, 53, 45, 37, 29,
	21, 13,  5, 28, 20, 12,  4
};

static const char totrot[] = 
{
	1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28
};

static const char pc2[] = 
{
	14, 17, 11, 24,  1,  5,
	 3, 28, 15,  6, 21, 10,
	23, 19, 12,  4, 26,  8,
	16,  7, 27, 20, 13,  2,
	41, 52, 31, 37, 47, 55,
	30, 40, 51, 45, 33, 48,
	44, 49, 39, 56, 34, 53,
	46, 42, 50, 36, 29, 32
};

static const char si[8][64] = 
{
    {
		/* S1 */
		14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7,
		 0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8,
		 4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0,
		15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13
    },
    {
		/* S2 */
		15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10,
		 3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5,
		 0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15,
		13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9
    },
    {
		/* S3 */
		10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8,
		13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1,
		13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7,
		 1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12
    },
    {
		/* S4 */
		 7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15,
		13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9,
		10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4,
		 3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14
	},
    {
		/* S5 */
		 2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9,
		14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6,
		 4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14,
		11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3
    },
    {
		/* S6 */
		12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11,
		10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8,
		 9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6,
		 4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13
    },
    {
		/* S7 */
		 4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1,
		13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6,
		 1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2,
		 6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12
    },
    {
		/* S8 */
		13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7,
		 1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2,
		 7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8,
		 2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11
    }
};

static const char p32i[] = 
{
	16,  7, 20, 21,
	29, 12, 28, 17,
	 1, 15, 23, 26,
	 5, 18, 31, 10,
	 2,  8, 24, 14,
	32, 27,  3,  9,
	19, 13, 30,  6,
	22, 11,  4, 25
};

static S32 (*sp)[64];

static char (*iperm)[16][8];
static char (*fperm)[16][8];


static const int bytebit[] = 
{
	0200,0100,040,020,010,04,02,01
};

static const int nibblebit[] = 
{
	 010,04,02,01
};

static void spinit() 
{
    char pbox[32];
    int p,i,s,j,rowcol;
    S32 val;

    for(p=0;p<32;p++){
        for(i=0;i<32;i++){
            if(p32i[i]-1 == p){
                pbox[p] = i;
                break;
            }
        }
    }

    for(s = 0; s < 8; s++){	
        for(i=0; i<64; i++){
            val = 0;
            rowcol = (i & 32) | ((i & 1) ? 16 : 0) | ((i >> 1) & 0xf);
            for(j=0;j<4;j++){
                if(si[s][rowcol] & (8 >> j)){
                    val |= 1L << (31 - pbox[4*s + j]);
                }
            }
            sp[s][i] = val;

        }
    }
}

static void perminit(char perm[16][16][8], const char p[64])
{
    int l, j, k;
    int i,m;

    for (i=0; i<16; i++)
        for (j=0; j<16; j++)
            for (k=0; k<8; k++)
                perm[i][j][k]=0;

    for (i=0; i<16; i++)
		for (j = 0; j < 16; j++)
            for (k = 0; k < 64; k++)
            {   
				l = p[k] - 1;	
                if ((l >> 2) != i)
                    continue;	
                if (!(j & nibblebit[l & 3]))
                    continue;
                m = k & 07;	
                perm[i][j][k>>3] |= bytebit[m];
            }
}

int desinit(desInst dinst) 
{
	if(sp != NULL){
		return 0;
	}

	sp = (S32 (*)[64])malloc(sizeof(S32) * 8 * 64);
	if(sp == NULL){
		return -1;
	}
	spinit();

	iperm = (char (*)[16][8])malloc(sizeof(char) * 16 * 16 * 8);
	if(iperm == NULL){
		free((char *)sp);
		return -1;
	}
	perminit(iperm,ip);

	fperm = (char (*)[16][8])malloc(sizeof(char) * 16 * 16 * 8);
	if(fperm == NULL){
		free((char *)sp);
		free((char *)iperm);
		return -1;
	}
	perminit(fperm,fp);

	return 0;
}

void dessetkey(desInst dinst, char *key) 
{
	char pc1m[56];
	char pcr[56];
	int i,j,l, m;

	for (i=0; i<16; i++)
		for (j=0; j<8; j++)
			dinst->kn[i][j] = 0;

	for (j=0; j<56; j++) {
		l = pc1[j]-1;		
		m = l & 07;	
		pc1m[j] = (key[l>>3] & bytebit[m]) ? 1 : 0;
	}

	for (i=0; i<16; i++) {	
		for (j=0; j<56; j++)
			pcr[j] = pc1m[(l=j+totrot[i])<(j<28? 28 : 56) ? l: l-28];

		for (j=0; j<48; j++) {	
			if (pcr[pc2[j]-1]) {
				l= j % 6;
				dinst->kn[i][j/6] |= bytebit[l] >> 2;
			}
		}
	}
}

static S32 f(U32 r, U8 subkey[8]) 
{
	U32 rval,rt;

	rt = (r >> 1) | ((r & 1) ? 0x80000000 : 0);
	rval = 0;
	rval |= sp[0][((rt >> 26) ^ *subkey++) & 0x3f];
	rval |= sp[1][((rt >> 22) ^ *subkey++) & 0x3f];
	rval |= sp[2][((rt >> 18) ^ *subkey++) & 0x3f];
	rval |= sp[3][((rt >> 14) ^ *subkey++) & 0x3f];
	rval |= sp[4][((rt >> 10) ^ *subkey++) & 0x3f];
	rval |= sp[5][((rt >> 6) ^ *subkey++) & 0x3f];
	rval |= sp[6][((rt >> 2) ^ *subkey++) & 0x3f];
	rt = (r << 1) | ((r & 0x80000000) ? 1 : 0);
	rval |= sp[7][(rt ^ *subkey) & 0x3f];
	return rval;
}

static void round1(desInst dinst, int num, U32 *block) 
{
    if(num & 1){
        block[1] ^= f(block[0],dinst->kn[num]);
    } else {
        block[0] ^= f(block[1],dinst->kn[num]);
    }
}

static void permute(char *inblock, char perm[16][16][8], char *outblock) 
{
    int i,j;
    char *ib, *ob;
    char *p, *q;

    if(perm == NULL){
        for(i=8; i!=0; i--)
            *outblock++ = *inblock++;
        return;
    }

    for (i=8, ob = outblock; i != 0; i--)
        *ob++ = 0;

    ib = inblock;

    for (j = 0; j < 16; j += 2, ib++) {
        ob = outblock;
        p = perm[j][(*ib >> 4) & 0x0f];
        q = perm[j + 1][*ib & 0x0f];

        for (i = 8; i != 0; i--){  
            *ob++ |= *p++ | *q++;
        }
    }
}

void endes(desInst dinst, char *block) 
{
	int i;
	U32 work[2] = {0}; 
	S32 tmp;

	permute(block, iperm, (char *)work);
	
	for (i=0; i<16; i++)
		round1(dinst,i,work);

	tmp = work[0];
	work[0] = work[1];
	work[1] = tmp;

	permute((char *)work, fperm, block);
}

